package cbbx_sm.simulation;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

import cbbx_sm.decision_maker.DecisionMakerFactory;
import cbbx_sm.decision_maker.IDecisionMaker;
import cbbx_sm.decision_maker.search.DynamicProgrammingLookahead;
import cbbx_sm.decision_maker.search.DynamicProgrammingTableEntry;
import cbbx_sm.decision_maker.search.States;
import cbbx_sm.parser.CameraData;
import cbbx_sm.parser.Parser;
import cbbx_sm.probabilistic_model.Cluster;
import cbbx_sm.probabilistic_model.Clustering;
import cbbx_sm.probabilistic_model.IPredictor;
import cbbx_sm.probabilistic_model.NoisyOrPredictor;
import cbbx_sm.probabilistic_model.ProbabilisticModel;
import cbbx_sm.probabilistic_model.SystemShortTermMemory;
import cbbx_sm.utils.LookaheadPredictorSerializator;
/**
 * TODO(ronenwai): see if that can actually be used in a reasonable way.
 * */
public class GenerateIllustrativeExample {
	public enum Decision {mostlikelyAlways, mostlikelyWhenMotion, lookahead, rr, rrGOUP, up};
	
	public static void main(String[] args) throws NumberFormatException, Exception{
		//String testDay = "20090526-short";
		String testDay = "20090528";
		//String testDay = "20090527";
		//DO NOT CHANGE
		String trainDay = "20090527";
		boolean runBaselineAlternatives = false;
		boolean floor = true;
		int numberOfSecondsCorrelation = 15;
		// Cameras scheduled.
		ArrayList<String> cameraIds = new ArrayList<String>();
		cameraIds.add("cam13");
		cameraIds.add("cam14");
		cameraIds.add("cam15");
		cameraIds.add("cam16");
		cameraIds.add("cam17");
		cameraIds.add("cam18");
		double utilityZoom = 1;
		double utilityUP = 0;
		
		double delta = 1;
		//for (double delta = 1; delta>=0d; delta-=0.1d){
		int numberOfTimeStampsLookAhead = 10;
		int clusters = 4;			  
		   // RR
		  performEvauation(cameraIds, clusters, Decision.rr, utilityZoom, utilityUP, delta, numberOfTimeStampsLookAhead, clusters, floor, testDay, trainDay);
				
	  utilityUP = 0;

		performEvauation(cameraIds, clusters, Decision.lookahead, utilityZoom, utilityUP, delta, 10, clusters, floor, testDay, trainDay);

	}
	public static void performEvauation(ArrayList<String> cameraIds, int numClusters, Decision decision, double utilityZoom, double utilityUP, 
			double delta, int numberOfTimeStampsLookAhead, int numberOfSecondsCorrelation, boolean floor, String testDay, String trainDay) throws NumberFormatException, Exception{
		
		// Camera Data.
		Hashtable<String, CameraData> cameraData = new Hashtable<String, CameraData>();
		ArrayList<CameraData> cameraDataList = new ArrayList<CameraData>();
		for (String cam: cameraIds){
			CameraData data = Parser.parseFile("data/cameraData/"+trainDay+cam.substring(3)+".txt", cam);
			cameraData.put(cam, data);
			cameraDataList.add(data);
		}		

		// Number of Clusters.
		int k=numClusters;
		
		// Each camera has a cluster.
		Hashtable<String, List<Cluster>> camClusters = new Hashtable<String, List<Cluster>>();
		for (String cam: cameraIds){
			System.out.println("Clustering cam "+cam+"...");
			List<Cluster> clusters = Clustering.kMeansClustering(cameraData.get(cam), k);
			camClusters.put(cam, clusters);
		}
		
		// All the clusters together.
		List<Cluster> fusedClusters = new Vector<Cluster>();
		for (String cam: cameraIds){
			fusedClusters.addAll(camClusters.get(cam));
		}

		// Load the probabilistic model or create it.
		String camerasConcat = "";
		int i=0;
		for (String cam: cameraIds){
			camerasConcat+=cam+(i<(cameraIds.size()-1)?"_":"");
			i++;
		}
		String modelFileName = "model"+camerasConcat+"_"+"k="+numClusters+"_kmeans.ser";
		ProbabilisticModel probModel = ProbabilisticModel.readUp(modelFileName);

		if (probModel == null){
			probModel = new ProbabilisticModel(cameraDataList, fusedClusters);
			probModel.setThreshold(0.0);
			ProbabilisticModel.writeDown(probModel, modelFileName);
			
			System.out.println("Probabilities computed.");
			System.out.println(probModel.toString());
		}
		
		
		// Load the test dataset.
		cameraData = new Hashtable<String, CameraData>();
		cameraDataList = new ArrayList<CameraData>();
		for (String cam: cameraIds){
			CameraData data = Parser.parseFile("data/cameraData/"+testDay+cam.substring(3)+".txt", cam);
			data.setImageFile("data/images/"+cam.substring(3)+".jpg");
			cameraData.put(cam, data);
			cameraDataList.add(data);
		}

		// Prepare the data structures for the scheduler simulator.
		List<List<Cluster>> clusters = new Vector<List<Cluster>>();
		for (String cam: cameraIds){
			clusters.add(camClusters.get(cam));
		}
		
		int numberOfBBoxes = 0;
		for (CameraData data: cameraDataList){
			numberOfBBoxes+=data.getNumberOfFrameWithBoundingBoxes();
		}
		System.out.println("NUMBER OF BBOX TO CATCH: " + numberOfBBoxes);
		System.out.println("Starting calculating probs...");
//		ProbabilisticModel probModel = new ProbabilisticModel();
//		probModel.computeProbabilities(cameraData, fusedClusters, (18 - 13) * 60 * 60);
		


		
		
		SystemShortTermMemory memory;
		IPredictor predictor;
		Scheduler scheduler;
		
		scheduler = new Scheduler(cameraDataList, probModel, "output/schedulerEvaluationResults.txt","/Volumes/Iomega_Ext Drive/20090528");

		memory = new SystemShortTermMemory(15);
		predictor = new NoisyOrPredictor(memory, probModel, 15, fusedClusters, true);
		


		
		System.out.println("Starting Simulation...");
		IDecisionMaker decisionMaker = null;
		switch (decision){
		case mostlikelyAlways:
			decisionMaker = DecisionMakerFactory.getMostLikelyClusterDecisionMaker(0);
			break;
		case mostlikelyWhenMotion:
			decisionMaker = DecisionMakerFactory.getMostLikelyClusterDecisionMaker(1);
			break;	
		case lookahead:
			Hashtable<String, States> s = new Hashtable<String, States>();
			Hashtable<String,DynamicProgrammingLookahead> table = new Hashtable<String,DynamicProgrammingLookahead>();
			
			
			for (String cam: cameraIds){
				if (cam.compareTo("cam13")!=0){
					continue;
				}
				// Load model to figure out which index was each cluster when single camera model was created.
				ProbabilisticModel singleCamModel = probModel.projectModel(cam);
				
				DynamicProgrammingLookahead camTable = LookaheadPredictorSerializator.readUpScoreArray(singleCamModel, utilityZoom, utilityUP, delta, numberOfTimeStampsLookAhead);
				ArrayList<double[]> rawStates = null;
				States camStates = null;
				Hashtable<Cluster, Integer> clusterIndex = null;
				if (camTable == null){
					LookaheadPredictorSerializator.generateLookaheadTable(singleCamModel, utilityZoom, utilityUP, numberOfTimeStampsLookAhead, delta);
				}
				camTable = LookaheadPredictorSerializator.readUpScoreArray(singleCamModel, utilityZoom, utilityUP, delta, numberOfTimeStampsLookAhead);
				s.put(cam, camStates);
				table.put(cam, camTable);	
				return;
			}
			
			Hashtable<String, Cluster> clusterIndex = new Hashtable<String, Cluster>();
			for (Cluster c : fusedClusters){
				clusterIndex.put(c.getUniqueId(), c);
			}
			
			decisionMaker = DecisionMakerFactory.getLookAheadDecisionMaker(
					cameraIds, table, clusterIndex, utilityZoom, utilityUP, delta, numberOfSecondsCorrelation, floor);
			break;
		case rr:
			decisionMaker = DecisionMakerFactory.getRoundRobinDecisionMaker(fusedClusters, false);
			break;
		case rrGOUP:
			decisionMaker = DecisionMakerFactory.getRoundRobinDecisionMaker(fusedClusters, true);
			break;
		case up:
			// Will go up for sure with a pro. cut off of 1.1
			decisionMaker = DecisionMakerFactory.getMostLikelyClusterDecisionMaker(1.1); 
			break;
		}

		scheduler.setDecisionMaker(decisionMaker);
		scheduler.setMemory(memory);
		scheduler.setPredictor(predictor);
		scheduler.sequentialRun();
		System.gc();
		System.out.println("Simulation Ended!");
				
	}	
}
